<template>
  <el-select ref="treeSelectRef" style="width: 100%;" clearable :value="selectLabel" :multiple="multiple"
    :placeholder="placeholder" :popper-class="popperClass" :collapse-tags="collapseTags"
    @remove-tag="handleSelectRemoveTag" @clear="handleSelectClear">
    <div class="tree-select-search" v-if="showSearch"> 
      <el-input placeholder="请输入关键字" v-model="treeFilterText" clearable size="mini"></el-input>
    </div>
    <el-option :value="selectOptionValue" class="tree-select-option-item">
      <div class="tree-select-tree-body">
        <el-tree id="tree-option" ref="optionTreeRef" highlight-current empty-text="加载中，请稍后..."
          :show-checkbox="showCheckbox" :data="treeData" :props="treeProps" :node-key="nodeKey"
          :filter-node-method="filterTreeNode" :default-expanded-keys="defaultExpandedKeys"
          @node-click="handleNodeClick" @check="handleTreeCheck">
        </el-tree>
      </div>
    </el-option>
  </el-select>
</template>
<script>


const defaultProps = {
  children: 'children', // 子级字段
  label: 'name' // 展示字段
}
export default {
  name: 'treeSelect',
  model: {
    prop: 'defaultValue',
    event: 'change'
  },
  data() {
    return {
      selectLabel: '', // 下拉框回显的值
      selectValue: '', // 下拉框选中的值
      selectOptionValue: undefined, // 下拉框options的值
      treeFilterText: '', // 树数据搜索内容
      defaultExpandedKeys: [], // 默认展开的数据
    }
  },
  props: {
    defaultValue: {
      type: [String, Number, Array]
    },
    treeData: {
      type: Array,
      default() {
        return []
      }
    },
    treeProps: {
      type: Object,
      default() {
        return defaultProps
      }
    },
    clearable: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    searchPlaceholder: {
      type: String,
      default: '请输入关键字'
    },
    collapseTags: {
      type: Boolean,
      default: true
    },
    showSearch: {
      type: Boolean,
      default: false
    },
    nodeKey: {
      type: String,
      default: 'id'
    }
  },
  watch: {
    defaultValue: {
      deep: true,
      immediate: true,
      handler(newVal, oldVal) {
        const { multiple } = this
        if(newVal) {
          if(!this.reloadTreeCheck) {
            this.handleSetTreeCheck(newVal)
          }
        } else {
          this.selectValue = multiple ? [] : ''
          this.selectLabel = multiple ? [] : ''
        }
      }
    },
    treeFilterText(value) {
      setTimeout(() => {
        this.handleInputChange(value)
      }, 300)
    }
  },
  methods: {
    // 多选模式下，点击移除单个tag
    handleSelectRemoveTag(tag) {
      this.reloadTreeCheck = false
      const { selectValue } = this
      if(isArray(selectValue)) {
        this.selectValue.shift()
      }
      this.$emit('removeTag', tag)
    },
    handleSelectClear() {
      this.reloadTreeCheck = false
      const { multiple } = this
      const value = multiple ? [] : ''
      this.treeFilterText = ''
      this.selectValue = [...value]
      this.selectLabel = [...value]
      this.$emit('change', value, null)
      this.$emit('clear')
    },
    handleNodeClick(node) {
      const { treeProps, multiple, nodeKey } = this
      if(multiple) return
      if(node.children && node.children.length > 0) return
      this.selectValue = node[nodeKey]
      this.selectLabel = node[treeProps.label]
      this.$emit('change', this.selectValue, node)
    },
    handleTreeCheck(node, values) {
      const { treeProps } = this
      const { checkedKeys, checkedNodes } = values
      this.selectValue = checkedKeys
      const labelValues = checkedNodes.map(nodeItem => {
        return nodeItem[treeProps.label]
      })
      this.selectLabel = labelValues
      this.$emit('change', checkedKeys, checkedNodes)
    },
    handleSetTreeNode(value) {
      const { optionTreeRef, treeProps } = this
      this.selectValue = value
      if(isArray(value)) {
        this.selectLabel = value.map(item => {
          const treeNode = optionTreeRef.getNode(item)
          return treeNode.data[treeProps.label]
        })
      } else {
        const treeNode = optionTreeRef.getNode(value)
        this.selectLabel = treeNode.data[treeProps.label]
      }
    },
    handleTreeExpandedKeys(value) {
      const { optionTreeRef, multiple } = this
      if(isArray(value) && multiple) {
        optionTreeRef.setCheckedKeys(value)
        value.forEach(item => {
          const treeNode = optionTreeRef.getNode(item)
          if(treeNode && treeNode.parent) {
            this.setTreeExpandKeys(treeNode.parent)
          }
        })
      } else {
        optionTreeRef.setCurrentKey(value)
        const treeNode = optionTreeRef.getNode(value)
        if(treeNode && treeNode.parent) {
          this.setTreeExpandKeys(treeNode.parent)
        }
      }
    },
    setTreeExpandKeys(node) {
      node.expanded = true
      if(node.parent) {
        this.setTreeExpandKeys(node.parent)
      }
    },
    filterTreeNode(value, data, node) {
      if(!value) {
        return true
      }
      const { treeProps: { label } } = this
      return data[label].indexOf(value) !== -1
    },
    handleSetTreeCheck(value) {
      if(!this.reloadTreeCheck && value) {
        this.$nextTick(() => {
          this.handleSetTreeNode(value)
          this.handleTreeExpandedKeys(value)
          this.reloadTreeCheck = true
        })
      }
    },
    handleInputChange(value) {
      this.optionTreeRef.filter(value)
    }
  },
  computed: {
    showCheckbox() {
      return this.multiple
    },
    popperClass() { // 下拉框自定义类名
      let classNames = ['custom-tree-select-popper']
      if (this.showSearch) {
        classNames.push('custom-tree-select-search')
      }
      return classNames.join(' ')
    },
    // 下拉框实例
    treeSelectRef() {
      return this.$refs.treeSelectRef
    },
    // 树结构实例
    optionTreeRef() {
      return this.$refs.optionTreeRef
    }
  },
  mounted() {
    // 重新加载树结构中， false-加载，true-不加载
    this.reloadTreeCheck = false
  }
}
function isArray(arg) {
  if(typeof Array.isArray === 'undefined') {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
  return Array.isArray(arg)
}
</script>
<style lang="less">
.custom-tree-select-search {
  .el-select-dropdown__list {
    padding: 0;
  }
}
.custom-tree-select-popper {
  .el-scrollbar {
    .el-select-dropdown__wrap {
      max-height: 365px !important;
    }
    .el-scrollbar__bar.is-vertical {
      z-index: 3;
    }
  }
}
</style>
<style lang="less" scoped>
.tree-select-search {
  position: sticky;
  top: 0;
  z-index: 2;
  display: block;
  padding: 6px;
  background: #fff;
}
.tree-select-option-item {
  background: #fff;
  overflow: scroll;
  height: 200px;
  overflow-x: hidden;
}
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item{
  height: auto;
  min-height: 200px;
  padding: 0;
  overflow: hidden;
}
</style>